import pathlib
import os
from typing import Optional
import logging # Import logging

logger = logging.getLogger(__name__) # Get logger instance

def file_path_to_module_path(root_dir: pathlib.Path, file_path: pathlib.Path) -> Optional[str]:
    """
    Converts an absolute file path within a project root to a Python module path.
    Prepends the root package name derived from the root_dir.
    """
    logger.debug(f"path_utils: Processing file '{file_path}' relative to root '{root_dir}'")

    if not file_path.is_file():
        logger.debug("path_utils: Not a file. Returning None.")
        return None
    if file_path.suffix != '.py':
        logger.debug(f"path_utils: Not a .py file (suffix: {file_path.suffix}). Returning None.")
        return None

    try:
        # Ensure paths are absolute for reliable comparison
        abs_root = root_dir.resolve()
        abs_file = file_path.resolve()
        relative_path = abs_file.relative_to(abs_root)
        logger.debug(f"path_utils: Attempting relative_to: file='{abs_file}', root='{abs_root}'")
        logger.debug(f"path_utils: Relative path is '{relative_path}'")
    except ValueError:
        # This happens if file_path is not inside root_dir
        logger.debug(f"path_utils: File '{abs_file}' is not inside root directory '{abs_root}'. Returning None.")
        return None
    except OSError as e:
        logger.error(f"path_utils: Error resolving paths for {file_path} relative to {root_dir}: {e}", exc_info=True)
        return None


    # --- Determine root package name (simple approach: name of root_dir) ---
    # Adjust if using src layout, etc.
    root_package_name = abs_root.name
    if not root_package_name: # Should not happen for valid directories
        logger.error(f"path_utils: Could not determine root package name from root directory '{abs_root}'.")
        return None

    module_parts = list(relative_path.parts[:-1]) # Directories relative to root
    module_name = relative_path.stem          # Filename without extension

    logger.debug(f"path_utils: Calculated parts: {module_parts}, stem: '{module_name}', root package: '{root_package_name}'")

    # --- Calculate the full path based on file type ---
    calculated_path: Optional[str] = None
    if module_name == "__init__":
        if not module_parts:
            # Root __init__.py case - module path IS the package name
            logger.debug(f"path_utils: Root __init__.py case.")
            calculated_path = root_package_name
        else:
            # Package __init__.py case - path is package dirs joined
            logger.debug(f"path_utils: Package __init__.py case.")
            # Join root name with intermediate parts
            calculated_path = ".".join([root_package_name] + module_parts)
    else:
        # Standard module file case - path includes file stem
        logger.debug(f"path_utils: Standard module file case.")
        # Join root name with intermediate parts and file stem
        calculated_path = ".".join([root_package_name] + module_parts + [module_name])


    # --- Validation ---
    if calculated_path is None:
        # This should ideally not happen if logic above is correct, but check anyway
        logger.warning(f"path_utils: Path calculation resulted in None unexpectedly for {file_path}")
        return None

    # Check for invalid characters (hyphens are common issues)
    # Allow underscores in module names
    if any(not part.isidentifier() or '-' in part for part in calculated_path.split('.')):
        # Relaxed check: isidentifier allows letters, numbers, underscores (starting with letter/underscore)
        invalid_parts = [part for part in calculated_path.split('.') if not part.isidentifier()]
        if invalid_parts:
            logger.warning(f"path_utils: Module path '{calculated_path}' contains invalid parts ({invalid_parts}) for file {file_path}. Returning None.")
            return None # Invalid characters found


    # --- Final Log and Return ---
    logger.debug(f"path_utils: Final calculated module path for '{file_path}' is: {calculated_path}")
    return calculated_path

# ... (rest of the file, e.g., ensure_absolute function) ...